#include <iostream>

#include "QueryExpressionParser.h"
#include "PKBStubForParentTest.h"

// Currently assumes the following SIMPLE source code:
// Procedure MyProc {
//1.	x = 2;
//2.	y = x;
//3.	while x {
//4.		x = x - 1;
//5.		y = y + x;
//6.		while y {
//7.			z = y - 1;
//			}
//		}
//8.	y = 5;
// }
PKBStubForParentTest::PKBStubForParentTest(void) {
	cout << "Constructing PKBStubForParentTest\n";

	mAssignNodeMap.insert( std::pair<int, TreeNode*>(1, createTree(AST_ASSIGN, "x", "2")) );
	mAssignNodeMap.insert( std::pair<int, TreeNode*>(2, createTree(AST_ASSIGN, "y", "x")) );
	mAssignNodeMap.insert( std::pair<int, TreeNode*>(4, createTree(AST_ASSIGN, "x", "x-1")) );
	mAssignNodeMap.insert( std::pair<int, TreeNode*>(5, createTree(AST_ASSIGN, "y", "y+x")) );
	mAssignNodeMap.insert( std::pair<int, TreeNode*>(7, createTree(AST_ASSIGN, "z", "y-1")) );
	mAssignNodeMap.insert( std::pair<int, TreeNode*>(8, createTree(AST_ASSIGN, "y", "5")) );
}


PKBStubForParentTest::~PKBStubForParentTest(void) {
}

TreeNode* PKBStubForParentTest::createTree(TreeNodeType nodeType, std::string varName, std::string expr) {
	TreeNode *root = NULL;
	TreeNode *leftChild = NULL;
	TreeNode *rightChild = NULL;
	QueryExpressionParser *queryExpParser = new QueryExpressionParser(this);

	root = new TreeNode(nodeType);

	int varIndex = getVarIndex(varName);
	leftChild = new TreeNode(AST_VAR, varIndex);

	rightChild = queryExpParser->convertExpressionToTree(expr);

	root->addChild(leftChild);
	root->addChild(rightChild);

	return root;
}

bool PKBStubForParentTest::isSubtree(TreeNode* mainTree, TreeNode* subTree, bool isPartialMatch) {
	// STUB
	return false;
}

// Currently assumes the following SIMPLE source code:
// Procedure MyProc {
//1.	x = 2;
//2.	y = x;
//3.	while x {
//4.		x = x - 1;
//5.		y = y + x;
//6.		while y {
//7.			z = y - 1;
//			}
//		}
//8.	y = 5;
// }
const Type::List::Line& PKBStubForParentTest::getLinesByEntity(EntityType entityType) {
	std::vector<int>* lines = new std::vector<int>();
	/*if (entityType == PROG_LINE) {
		lines->push_back(1);
		lines->push_back(2);
		lines->push_back(3);
		lines->push_back(4);
		lines->push_back(5);
		lines->push_back(6);
		lines->push_back(7);
		lines->push_back(8);
	}
	if (entityType == STMT) {
		lines->push_back(1);
		lines->push_back(2);
		lines->push_back(3);
		lines->push_back(4);
		lines->push_back(5);
		lines->push_back(6);
		lines->push_back(7);
		lines->push_back(8);
	}*/
	if (entityType == Type::Entity::ASSIGNMENT_LINE_ENTITY) {
		lines->push_back(1);
		lines->push_back(2);
		lines->push_back(4);
		lines->push_back(5);
		lines->push_back(7);
		lines->push_back(8);
	}
	if (entityType == Type::Entity::WHILE_LINE_ENTITY) {
		lines->push_back(3);
		lines->push_back(6);
	}
	return *lines;
}

// Constants available: 1, 2, 5
const std::vector<int>& PKBStubForParentTest::getConstants() {
	std::vector<int>* constants = new std::vector<int>();
	constants->push_back(1);
	constants->push_back(2);
	constants->push_back(5);
	return *constants;
}

// (varName = varIndex) Assumes:
// x = 1, y = 2, z = 3.
int PKBStubForParentTest::getVarIndex(std::string varName) {
	if (varName == "x") {
		return 1;
	} else if (varName == "y") {
		return 2;
	} else if (varName == "z") {
		return 3;
	} else {
		return -1;
	}
}

// (varName = varIndex) Assumes:
// x = 1, y = 2, z = 3.
std::vector<int> PKBStubForParentTest::getVarIndexes() {
	std::vector<int> varIndexes;
	varIndexes.push_back(1);
	varIndexes.push_back(2);
	varIndexes.push_back(3);
	return varIndexes;
}

// (varName = varIndex) Assumes:
// x = 1, y = 2, z = 3.
std::string PKBStubForParentTest::getVarName(int varIndex) {
	switch (varIndex) {
	case 1: return "x"; break;
	case 2: return "y"; break;
	case 3: return "z"; break;
	default: return ""; break;
	}
}

// Assumes the following Parent relationships:
// Parent(3,4), Parent(3,5), Parent(3,6), Parent(6,7)
// e.g. getParent(4) will return 3.
int PKBStubForParentTest::getParent(Type::Index::Line index) {
	switch (index) {
	case 4:
		// Fallthrough
	case 5:
		// Fallthrough
	case 6:
		return 3;
		break;
	case 7:
		return 6;
		break;
	default:
		return -1;
		break;
	}
}

// Assumes the following Parent* relationships:
// Parent*(3,4), Parent*(3,5), Parent*(3,6), Parent*(3,7), Parent*(6,7)
// e.g. getParentStar(7) will return a vector [3, 6].
std::vector<int> PKBStubForParentTest::getParentStar(Type::Index::Line index) {
	std::vector<int> lines;
	switch (index) {
	case 4:
		// Fallthrough
	case 5:
		// Fallthrough
	case 6:
		lines.push_back(3);
		break;
	case 7:
		lines.push_back(3);
		lines.push_back(6);
		break;
	default:
		break;
	}
	return lines;
}

// Assumes the following Parent relationships:
// Parent(3,4), Parent(3,5), Parent(3,6), Parent(6,7)
// e.g. getChild(3) will return a vector [4, 5, 6].
std::vector<int> PKBStubForParentTest::getChild(Type::Index::Line index) {
	std::vector<int> lines;
	switch (index) {
	case 3:
		lines.push_back(4);
		lines.push_back(5);
		lines.push_back(6);
		break;
	case 6:
		lines.push_back(7);
		break;
	default:
		break;
	}
	return lines;
}

// Assumes the following Parent* relationships:
// Parent*(3,4), Parent*(3,5), Parent*(3,6), Parent*(3,7), Parent*(6,7)
// e.g. getChildStar(3) will return a vector [4, 5, 6, 7].
std::vector<int> PKBStubForParentTest::getChildStar(Type::Index::Line index) {
	std::vector<int> lines;
	switch (index) {
	case 3:
		lines.push_back(4);
		lines.push_back(5);
		lines.push_back(6);
		lines.push_back(7);
		break;
	case 6:
		lines.push_back(7);
		break;
	default:
		break;
	}
	return lines;
}

// Assumes the following Parent relationships:
// Parent(3,4), Parent(3,5), Parent(3,6), Parent(6,7)
// e.g. hasParent(4) returns true and hasParent(2) returns false.
bool PKBStubForParentTest::hasParent(Type::Index::Line index) {
	switch (index) {
	case 4:
		// Fallthrough
	case 5:
		// Fallthrough
	case 6:
		// Fallthrough
	case 7:
		return true;
		break;
	default:
		return false;
		break;
	}
}

// Assumes the following Parent relationships:
// Parent(3,4), Parent(3,5), Parent(3,6), Parent(6,7)
// e.g. hasChild(3) returns true and hasChild(4) returns false.
bool PKBStubForParentTest::hasChild(Type::Index::Line index) {
	switch (index) {
	case 3:
		// Fallthrough
	case 6:
		return true;
		break;
	default:
		return false;
		break;
	}
}

// Assumes the following Parent relationships:
// Parent(3,4), Parent(3,5), Parent(3,6), Parent(6,7)
// e.g. isParent(3,4) returns true and isParent(3,7) returns false.
bool PKBStubForParentTest::isParent(Type::Index::Line index1, Type::Index::Line index2) {
	switch (index1) {
	case 3:
		switch (index2) {
		case 4:
			// Fallthrough
		case 5:
			// Fallthrough
		case 6:
			return true;
			break;
		default:
			return false;
			break;
		}
		break;
	case 6:
		switch (index2) {
		case 7:
			return true;
			break;
		default:
			return false;
			break;
		}
		break;
	default:
		return false;
		break;
	}
}

// Assumes the following Parent* relationships:
// Parent*(3,4), Parent*(3,5), Parent*(3,6), Parent*(3,7), Parent*(6,7)
// e.g. isParentStar(3,7) returns true and isParent(3,8) returns false.
bool PKBStubForParentTest::isParentStar(Type::Index::Line index1, Type::Index::Line index2) {
	switch (index1) {
	case 3:
		switch (index2) {
		case 4:
			// Fallthrough
		case 5:
			// Fallthrough
		case 6:
			// Fallthrough
		case 7:
			return true;
			break;
		default:
			return false;
			break;
		}
		break;
	case 6:
		switch (index2) {
		case 7:
			return true;
			break;
		default:
			return false;
			break;
		}
		break;
	default:
		return false;
		break;
	}
}

// Assumes the following Follows relationships:
// Follows(1,2), Follows(2,3), Follows(3,8), Follows(4,5), Follows(5,6)
// getFollows(1) will return 2.
Type::Index::Line PKBStubForParentTest::getFollows(Type::Index::Line index) {
	if (index == 1) {
		return 2;
	} else if (index == 2) {
		return 3;
	} else if (index == 3) {
		return 8;
	} else if (index == 4) {
		return 5;
	} else if (index == 5) {
		return 6;
	} else {
		return -1;
	}
}

// Assumes the following Follows relationships:
// Follows*(1,2), Follows*(1,3), Follows*(1,8), Follows*(2,3), Follows*(2,8),
// Follows*(3,8), Follows*(4,5), Follows*(4,6), Follows*(5,6)
// getFollowStar(1) will return [2,3,8].
Type::List::Line PKBStubForParentTest::getFollowStar(Type::Index::Line index) {
	std::vector<int> resultIndexes;
	if (index == 1) {
		resultIndexes.push_back(2);
		resultIndexes.push_back(3);
		resultIndexes.push_back(8);
	} else if (index == 2) {
		resultIndexes.push_back(3);
		resultIndexes.push_back(8);
	} else if (index == 3) {
		resultIndexes.push_back(8);
	} else if (index == 4) {
		resultIndexes.push_back(5);
		resultIndexes.push_back(6);
	} else if (index == 5) {
		resultIndexes.push_back(6);
	} 
	return resultIndexes;
}

// Assumes the following Follows relationships:
// Follows(1,2), Follows(2,3), Follows(3,8), Follows(4,5), Follows(5,6)
// getFollowedBy(2) will return 1. 
Type::Index::Line PKBStubForParentTest::getFollowedBy(Type::Index::Line index) {
	if (index == 2) {
		return 1;
	} else if (index == 3) {
		return 2;
	} else if (index == 8) {
		return 3;
	} else if (index == 5) {
		return 4;
	} else if (index == 6) {
		return 5;
	} else {
		return -1;
	}
}

// Assumes the following Follows relationships:
// Follows*(1,2), Follows*(1,3), Follows*(1,8), Follows*(2,3), Follows*(2,8),
// Follows*(3,8), Follows*(4,5), Follows*(4,6), Follows*(5,6)
// getFollowedByStar(3) will return [1,3].
Type::List::Line PKBStubForParentTest::getFollowedByStar(Type::Index::Line index) {
	std::vector<int> resultIndexes;
	switch (index) {
	case 2:
		resultIndexes.push_back(1);
		break;
	case 3:
		resultIndexes.push_back(1);
		resultIndexes.push_back(2);
		break;
	case 5:
		resultIndexes.push_back(4);
		break;
	case 6:
		resultIndexes.push_back(4);
		resultIndexes.push_back(5);
		break;
	default: 
		resultIndexes.push_back(1);
		resultIndexes.push_back(2);
		resultIndexes.push_back(3);
		break;
	}
	return resultIndexes;
}

// Assumes the following Follows relationships:
// Follows(1,2), Follows(2,3), Follows(3,8), Follows(4,5), Follows(5,6)
// isFollows(1,2) will return true, isFollows(3,4) will return false.
bool PKBStubForParentTest::isFollows(Type::Index::Line index1, Type::Index::Line index2) {
	if (index1 == 1 && index2 == 2) {
		return true;
	} else if (index1 == 2 && index2 == 3) {
		return true;
	} else if (index1 == 3 && index2 == 8) {
		return true;
	} else if (index1 == 4 && index2 == 5) {
		return true;
	} else if (index1 == 5 && index2 == 6) {
		return true;
	} else {
		return false;
	}
}

// Assumes the following Follows relationships:
// Follows*(1,2), Follows*(1,3), Follows*(1,8), Follows*(2,3), Follows*(2,8),
// Follows*(3,8), Follows*(4,5), Follows*(4,6), Follows*(5,6)
// isFollowStar(3,8) will return true, isFollowStar(3,4) will return false.
bool PKBStubForParentTest::isFollowStar(Type::Index::Line index1, Type::Index::Line index2) {
	switch (index1) {
	case 1:
		switch (index2) {
		case 2:
			// Fallthrough
		case 3:
			// Fallthrough
		case 8:
			// Fallthrough
			return true;
			break;
		default: 
			return false;
			break;
		}
	case 2:
		switch (index2) {
		case 2:
			// Fallthrough
		case 8:
			return true;
			break;
		default:
			return false;
			break;
		}
	case 3:
		switch (index2) {
		case 8:
			return true;
			break;
		default:
			return false;
			break;
		}
	case 4:
		switch (index2) {
		case 5:
			// Fallthrough
		case 6:
			return true;
			break;
		default:
			return false;
			break;
		}
	case 5:
		switch (index2) {
		case 6:
			return true;
			break;
		default:
			return false;
			break;
		}
	default: 
		return false;
		break;
	}
}

// Assumes the following Modifies relationships:
// Modifies(1,"x"), Modifies(2,"y"), Modifies(4,"x"), Modifies(5,"y"), Modifies(7,"z"), Modifies(8,"y")
// Modifies(3,"x"), Modifies(3,"y"), Modifies(3,"z"), Modifies(6,"z")
// where (varName = varIndex) x = 1, y = 2, z = 3.
// e.g. isLineModifies(1,1) returns true and isLineModifies(2,1) returns false.
bool PKBStubForParentTest::isLineModifies(int lineIndex, int varIndex) {
	switch (lineIndex) {
	case 1:
		if (varIndex == 1) {
			return true;
		} else {
			return false;
		}
		break;
	case 2:
		if (varIndex == 2) {
			return true;
		} else {
			return false;
		}
		break;
	case 3:
		if (varIndex == 1) {
			return true;
		} else if (varIndex == 2) {
			return true;
		} else if (varIndex == 3) {
			return true;
		} else {
			return false;
		}
	case 4:
		if (varIndex == 1) {
			return true;
		} else {
			return false;
		}
		break;
	case 5:
		if (varIndex == 2) {
			return true;
		} else {
			return false;
		}
		break;
	case 6:
		if (varIndex == 3) {
			return true;
		} else {
			return false;
		}
	case 7:
		if (varIndex == 3) {
			return true;
		} else {
			return false;
		}
		break;
	case 8: 
		if (varIndex == 2) {
			return true; 
		} else {
			return false;
		}
		break;
	default: 
		return false; 
		break;
	}
}

// Assumes the following Uses relationships:
// Uses(2,"x"), Uses(3,"x"), Uses(4,"x"), Uses(5,"y"), Uses(5,"x"), Uses(6,"y"), Uses(7,"y")
// where (varName = varIndex) x = 1, y = 2, z = 3.
// e.g. isLineUses(3,1) returns true and isLineUses(6,1) returns false.
bool PKBStubForParentTest::isLineUses(int lineIndex, int varIndex) {
	switch (lineIndex) {
	case 2:
		if (varIndex == 1) return true;
		else return false;
		break;
	case 3:
		if (varIndex == 1) return true;
		else return false;
		break;
	case 4:
		if (varIndex == 1) return true;
		else return false;
		break;
	case 5:
		if (varIndex == 2) return true;
		else if (varIndex == 1) return true;
		else return false;
		break;
	case 6:
		if (varIndex == 2) return true;
		else return false;
		break;
	case 7:
		if (varIndex == 2) return true;
		else return false;
		break;
	default: 
		return false; 
		break;
	}
}

// Assumes the following (ASSIGN ONLY) patterns:
// 1 a("x","2"), 2 a("y","x"), 4 a("x","x-1"), 5 a("y","y+x"), 7 a("z","y-1"), 8 a("y","5")
// where (varName = varIndex) x = 1, y = 2, z = 3.
Type::List::Line PKBStubForParentTest::getLineIndexForPattern(PatternType patternType, Type::Index::Var varIndex, TreeNode* exprRoot, bool isPartialMatch) {
	std::vector<int> resultLines;
	if (patternType == PATTERN_ASSIGN) {
		if (varIndex == 1) {
			// x
			if ( (exprRoot->getNodeType() == AST_CONST) && (exprRoot->getContent() == 2) ) {
				// 1 a("x","2")
				resultLines.push_back(1);
			}
			if ( (exprRoot->getNodeType() == AST_MINUS) && (exprRoot->getNumberOfChildren() == 2) ) {
				TreeNode *leftChild = exprRoot->getChild().at(0);
				TreeNode *rightChild = exprRoot->getChild().at(1);
				if ( (leftChild->getNodeType() == AST_VAR) && (leftChild->getContent() == 1) &&
					(rightChild->getNodeType() == AST_CONST) && (rightChild->getContent() == 1) ) {
						// 4 a("x","x-1)
						resultLines.push_back(4);
				}
			}
		} else if (varIndex == 2) {
			// y
			if ( (exprRoot->getNodeType() == AST_VAR) && (exprRoot->getContent() == 1) ) {
				// 2 a("y","x")
				resultLines.push_back(2);
			}
			if ( (exprRoot->getNodeType() == AST_PLUS) && (exprRoot->getNumberOfChildren() == 2) ) {
				TreeNode *leftChild = exprRoot->getChild().at(0);
				TreeNode *rightChild = exprRoot->getChild().at(1);
				if ( (leftChild->getNodeType() == AST_VAR) && (leftChild->getContent() == 2) &&
					(rightChild->getNodeType() == AST_VAR) && (rightChild->getContent() == 1) ) {
						// 5 a("y","y+x")
						resultLines.push_back(5);
				}
			}
			if ( (exprRoot->getNodeType() == AST_CONST) && (exprRoot->getContent() == 5) ) {	
				// 8 a("y","5")
				resultLines.push_back(8);
			}
		} else if (varIndex == 3) {
			// z
			if ( (exprRoot->getNodeType() == AST_MINUS) && (exprRoot->getNumberOfChildren() == 2) ) {
				TreeNode *leftChild = exprRoot->getChild().at(0);
				TreeNode *rightChild = exprRoot->getChild().at(1);
				if ( (leftChild->getNodeType() == AST_VAR) && (leftChild->getContent() == 2) &&
					(rightChild->getNodeType() == AST_CONST) && (rightChild->getContent() == 1) ) {
						// 7 a("z","y-1")
						resultLines.push_back(7);
				}
			}
		}
	} 

	return resultLines;
}